﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using agvcontroller.Common;
using agvcontroller.Algorothm;
using agvcontroller.Control;
using agvcontroller.Log;

namespace agvcontroller.Process
{
    class CRouteManager
    {
        CPathFinder PathFinder;

        public CNodeDic NodeDic;
        public CEdgeDic EdgeDic;
        CVehicleDic VehicleDic;
        CChargerDic ChargerDic;

        int[] NodeIndex;
        int[,] NodeArray;

        const int INFINITY = int.MaxValue;

        Logger Log;

        private static CRouteManager uniqueInstance;

        public static CRouteManager getInstance()
        {
            if (uniqueInstance == null)
            {
                uniqueInstance = new CRouteManager();
            }
            return uniqueInstance;
        }

        public CRouteManager()
        {
            Log = new Logger("RouteManager");

            PathFinder = new CPathFinder(enumAlgorithm.FLOYD);

            NodeDic = CNodeDic.getInstance();
            EdgeDic = CEdgeDic.getInstance();
            VehicleDic = CVehicleDic.getInstance();
            ChargerDic = CChargerDic.getInstance();

            NodeArray = ConvertArray();
            NodeIndex = GetNodeIndex();
        }

        public List<CNode> SearchNearestRoute(CJob job , CVehicle assignedVehicle)
        {
            try
            {
                List<CNode> route = new List<CNode>();
                int distance = INFINITY;

                int[] routeID = PathFinder.SearchShortestPath(NodeArray, assignedVehicle.CurNode, job.Dest, out distance);

                foreach (int id in routeID)
                {
                    route.Add(NodeDic[id]);
                }

                if (route.Count <= 1)
                {
                    route = null;
                }

                return route;
            }
            catch (Exception ex)
            {
                Log.Exception(string.Format("{0},{1}", ex.Message, ex.StackTrace));
                return null;
            }
        }

        public List<int> DivideRoute(List<int> route, CVehicle assignedVehicle)
        {
            List<int> divideroute = new List<int>();


            return divideroute;
        }

        public CVehicle SearchNearestVehicle(CJob job)
        {
            int shortDistance = int.MaxValue;
            int tmpDistance = 0;
            CVehicle nearestVehicle = null;

            try
            {
                foreach (KeyValuePair<int, CVehicle> vehicle in VehicleDic)
                {
                    if (vehicle.Value.Connection != enumConnect.CONNECTION)
                        continue;

                    if (vehicle.Value.Use != true)
                        continue;

                    if (vehicle.Value.Control != enumContolState.IDLE)
                        continue;

                    if (vehicle.Value.BatteryLow == true)
                        continue;

                    tmpDistance = SearchDistance(vehicle.Value.CurNode, job.Source);

                    if (tmpDistance < shortDistance)
                    {
                        shortDistance = tmpDistance;
                        nearestVehicle = vehicle.Value;
                    }
                }

                return nearestVehicle;
            }
            catch (Exception ex)
            {
                Log.Exception(string.Format("{0},{1}", ex.Message, ex.StackTrace));
                return null;
            }
        }

        public int SearchNearestCharger(CVehicle vehicle)
        {
            int shortDistance = INFINITY;
            int tmpDistance = 0;

            foreach (KeyValuePair<int, CCharger> charger in ChargerDic)
            {
                if (charger.Value.Control != enumContolState.IDLE)
                    continue;
                if (charger.Value.Use != true)
                    continue;

                tmpDistance = SearchDistance(vehicle.CurNode, charger.Value.NodeID);

                if (tmpDistance < shortDistance)
                {
                    shortDistance = tmpDistance;
                }
            }

            return shortDistance;
        }

        List<int> SearchRoute(int sourcenodeid, int middlenodeid, int destnodeid)
        {
            List<int> route = new List<int>();

            return route;
        }

        int SearchDistance(int fromnodeid, int tonodeid)
        {
            int distance = INFINITY;
            
            PathFinder.SearchShortestPath(NodeArray, fromnodeid, tonodeid, out distance);

            return distance;
        }

        public List<enumVehicleDirect> SearchDirect(List<CNode> route, CVehicle assignedVehicle)
        {
            List<enumVehicleDirect> direct = new List<enumVehicleDirect>();

            return direct;
        }

        public List<int> SearchSpeed(List<CNode> route, CVehicle assignedVehicle)
        {
            List<int> speed = new List<int>();

            return speed;
        }

        public List<enumAction> SearchAction(List<CNode> route, CVehicle assignedVehicle)
        {
            List<enumAction> action = new List<enumAction>();

            return action;
        }

        public int UpdateNodeInfo(CVehicle vehicle , int nodeno)
        {
            foreach (KeyValuePair<int, CNode> node in NodeDic)
            {
                if (node.Value.Occupied_Vechile == vehicle.ID)
                    node.Value.Occupied_Vechile = CReturnCode.NOEXIST;
            }

            NodeDic[nodeno].Occupied_Vechile = vehicle.ID;
            NodeDic[nodeno].Reserved_Vechile = CReturnCode.NOEXIST;

            return CReturnCode.SUCCESS;
        }

        int[] GetNodeIndex()
        {
            int[] result = new int[NodeDic.Count];

            int i = 0;
            foreach (KeyValuePair<int, CNode> node in NodeDic)
            {
                result[i++] = node.Value.ID;
            }

            return result;
        }

        /// <summary>
        /// Node 정보를 배열로 변환
        /// </summary>
        /// <param name="metrix"></param>
        int[,] ConvertArray()
        {
            int[,] result = new int[NodeDic.Count, NodeDic.Count];

            foreach (KeyValuePair<int, CEdge> edge in EdgeDic)
            {
                int idxFrom = GetIndex(edge.Value.From);
                int idxTo = GetIndex(edge.Value.To);
                result[idxFrom, idxTo] = edge.Value.Distance;
            }

            return result;
        }

        /// <summary>
        /// Node 순서를 가진 배열인 NodeIndex에서 해당 Node가 배열의 몇번째 방인지 반환함.
        /// </summary>
        /// <param name="nodeNo">방번호를 알고자 하는 Node No</param>
        /// <returns>배열의 방 번호를 반환</returns>
        int GetIndex(int nodeNo)
        {
            for (int i = 0; i < NodeIndex.Length; i++)
            {
                if (nodeNo == NodeIndex[i])
                    return i;
            }

            return CReturnCode.NOEXIST;
        }

        int GetNodeNobyIndex(int index)
        {
            return NodeIndex[index];
        }
    }
}
